package com.x.provider.oss.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.core.type.TypeReference;
import com.x.core.domain.SuggestionTypeEnum;
import com.x.core.utils.BeanUtil;
import com.x.core.utils.JsonUtil;
import com.x.core.web.api.R;
import com.x.core.web.page.PageHelper;
import com.x.core.web.page.PageLimit;
import com.x.provider.api.oss.model.dto.green.AddGreenNotifyRequestDTO;
import com.x.provider.api.oss.model.dto.green.ContentGreenResultDTO;
import com.x.provider.oss.configure.OssConfig;
import com.x.provider.oss.mapper.ContentMapper;
import com.x.provider.oss.mapper.ContentReviewDetailMapper;
import com.x.provider.oss.mapper.ContentReviewNotifyMapper;
import com.x.provider.oss.model.bo.GreenContentRequestBO;
import com.x.provider.oss.model.domain.Content;
import com.x.provider.oss.model.domain.ContentGreenDetail;
import com.x.provider.oss.model.domain.ContentGreenNotify;
import com.x.provider.oss.model.query.ContentQuery;
import com.x.provider.oss.model.query.ContentGreenDetailQuery;
import com.x.provider.oss.model.query.ContentGreenNotifyQuery;
import com.x.provider.oss.service.ContentGreenService;
import com.x.provider.oss.service.RedisKeyService;
import com.x.redis.service.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import java.util.Date;
import java.util.List;

/**
 * @author: liushenyi
 * @date: 2022/08/17/10:53
 */
@Slf4j
@Service
public class ContentGreenServiceImpl implements ContentGreenService {

    private static final Integer MAX_LIMIT_SIZE = 1000;

    private final RedisService redisService;
    private final RedisKeyService redisKeyService;
    private final ContentMapper contentMapper;
    private final ContentReviewDetailMapper contentReviewDetailMapper;
    private final ContentReviewNotifyMapper contentReviewResultNotifyMapper;
    private final RestTemplate restTemplate;
    private final OssConfig ossConfig;

    public ContentGreenServiceImpl(RedisService redisService,
                                   RedisKeyService redisKeyService,
                                   ContentMapper contentMapper,
                                   ContentReviewDetailMapper contentReviewDetailMapper,
                                   ContentReviewNotifyMapper contentReviewNotifyMapper,
                                   RestTemplate restTemplate,
                                   OssConfig ossConfig){
        this.redisService = redisService;
        this.redisKeyService = redisKeyService;
        this.contentMapper = contentMapper;
        this.contentReviewDetailMapper = contentReviewDetailMapper;
        this.contentReviewResultNotifyMapper = contentReviewNotifyMapper;
        this.restTemplate = restTemplate;
        this.ossConfig = ossConfig;
    }

    @Override
    public void greenContent(GreenContentRequestBO greenContentRequestBO) {
        Content content = getContent(ContentQuery.builder().contentGroup(greenContentRequestBO.getContentGroup()).contentKey(greenContentRequestBO.getContentKey()).build());
        if (content != null && content.getReviewed()){
            return;
        }
        if (content == null ){
            content = BeanUtil.prepare(greenContentRequestBO, Content.class);
            content.setReviewed(true);
            insertContent(content);
        }
        else{
            content.setSuggestion(greenContentRequestBO.getSuggestion());
            content.setReviewed(true);
            contentMapper.updateById(content);
        }
    }

    public List<ContentGreenDetail> list(ContentGreenDetailQuery query){
        return contentReviewDetailMapper.selectList(build(query));
    }

    @Override
    public void addGreenNotify(AddGreenNotifyRequestDTO addGreenNotifyRequestDTO){
        addGreenNotifyRequestDTO.getContentList().forEach(item -> {
            Content content = getContent(ContentQuery.builder().contentGroup(item.getContentGroupEnum()).contentKey(item.getContentKey()).build());
            if (content == null){
                insertContent(Content.builder().mimeType(item.getMimeType()).reviewed(false).suggestion(SuggestionTypeEnum.NONE.name()).contentKey(item.getContentKey()).contentGroup(item.getContentGroupEnum()).build());
            }
        });
        ContentGreenNotify contentReviewNotify = ContentGreenNotify.builder()
                .notifyUrl(addGreenNotifyRequestDTO.getNotifyUrl())
                .extra(addGreenNotifyRequestDTO.getExtra())
                .reviewed(false)
                .contentListJson(JsonUtil.toJSONString(addGreenNotifyRequestDTO.getContentList())).build();
            contentReviewResultNotifyMapper.insert(contentReviewNotify);
    }

    @Override
    public void updateContentGreenResult(){
        LambdaQueryWrapper<ContentGreenNotify> queryWrapper = build(ContentGreenNotifyQuery.builder().reviewed(false).pageLimit(new PageLimit(0L, MAX_LIMIT_SIZE)).build());
        List<ContentGreenNotify> contentReviewNotifyList = contentReviewResultNotifyMapper.selectList(queryWrapper);
        if (contentReviewNotifyList.isEmpty()){
            return;
        }
        contentReviewNotifyList.forEach(item -> {
            List<AddGreenNotifyRequestDTO.ContentDTO> contentList = JsonUtil.parseObject(item.getContentListJson(), new TypeReference<>() {});
            SuggestionTypeEnum suggestionTypeEnum = SuggestionTypeEnum.PASS;
            boolean allReviewed = true;
            for (AddGreenNotifyRequestDTO.ContentDTO c: contentList) {
                Content content = getContent(ContentQuery.builder().contentGroup(c.getContentGroupEnum()).contentKey(c.getContentKey()).build());
                if (content == null || !content.getReviewed()){
                    allReviewed = false;
                    break;
                }
                if (!SuggestionTypeEnum.PASS.name().equals(content.getSuggestion())){
                    suggestionTypeEnum = SuggestionTypeEnum.valueOf(content.getSuggestion());
                }
            }
            if (allReviewed){
                item.setSuggestion(suggestionTypeEnum.name());
                item.setReviewed(true);
                contentReviewResultNotifyMapper.updateById(item);
            }
        });
    }

    @Override
    public void notifyContentGreenResult(){
        LambdaQueryWrapper<ContentGreenNotify> queryWrapper = build(ContentGreenNotifyQuery.builder().endNotify(false).reviewed(true).pageLimit(new PageLimit(0L, MAX_LIMIT_SIZE)).build());
        List<ContentGreenNotify> contentReviewNotifyList = contentReviewResultNotifyMapper.selectList(queryWrapper);
        if (contentReviewNotifyList.isEmpty()){
            return;
        }
        contentReviewNotifyList.forEach(item -> {
            ContentGreenResultDTO contentReviewResultDTO = ContentGreenResultDTO.builder().suggestion(item.getSuggestion()).contentListJson(item.getContentListJson())
                    .extra(item.getExtra()).build();
            item.setLastNotifyDate(new Date());
            try {
                restTemplate.postForEntity(item.getNotifyUrl(), contentReviewResultDTO, R.class);
                item.setNotifySuccess(true);
                item.setEndNotify(true);
            }
            catch (Exception e){
                log.error(e.getMessage(), e);
                item.setNotifySuccess(false);
                item.setRetryCount(item.getRetryCount() + 1);
                if (item.getRetryCount() > ossConfig.getMaxReviewNotifyRetryCount()){
                    item.setEndNotify(true);
                }
            }
            contentReviewResultNotifyMapper.updateById(item);
        });
    }

    private void insertContent(Content content){
        try {
            contentMapper.insert(content);
        }
        catch (DataIntegrityViolationException ex){

        }
    }

    private LambdaQueryWrapper<ContentGreenDetail> build(ContentGreenDetailQuery contentReviewDetailQuery){
        LambdaQueryWrapper<ContentGreenDetail> queryWrapper = new LambdaQueryWrapper<>();
        if (contentReviewDetailQuery.getContentId() != null){
            queryWrapper = queryWrapper.eq(ContentGreenDetail::getContentId, contentReviewDetailQuery.getContentId());
        }
        return queryWrapper;
    }

    private Content getContent(ContentQuery query){
        return contentMapper.selectOne(build(query));
    }

    private LambdaQueryWrapper<Content> build(ContentQuery contentQuery){
        LambdaQueryWrapper<Content> query = new LambdaQueryWrapper<>();
        if (!StringUtils.isEmpty(contentQuery.getContentKey())){
            query = query.eq(Content::getContentKey, contentQuery.getContentKey());
        }
        if (!StringUtils.isEmpty(contentQuery.getContentGroup())){
            query = query.eq(Content::getContentGroup, contentQuery.getContentGroup());
        }
        return query;
    }

    private LambdaQueryWrapper<ContentGreenNotify> build(ContentGreenNotifyQuery notifyQuery){
        LambdaQueryWrapper<ContentGreenNotify> query = new LambdaQueryWrapper<>();
        if (notifyQuery.getEndNotify() != null){
            query = query.eq(ContentGreenNotify::getEndNotify, notifyQuery.getEndNotify());
        }
        if (notifyQuery.getReviewed() != null){
            query = query.eq(ContentGreenNotify::getReviewed, notifyQuery.getReviewed());
        }
        final String pageLimitSql = PageHelper.toPageLimitSql(notifyQuery.getPageLimit());
        if (!StringUtils.isEmpty(pageLimitSql)){
            query = query.last(pageLimitSql);
        }
        return query;
    }
}
